package my2Cents.tools;

import java.awt.event.MouseEvent;
import java.awt.geom.GeneralPath;

import my2Cents.KeyTrigger;
import my2Cents.Sandbox;
import my2Cents.Undo;
import my2Cents.Util;
import my2Cents.properties.CheckBox;
import my2Cents.properties.KeyAssign;
import my2Cents.properties.Slider;
import net.phys2d.math.Vector2f;
import net.phys2d.raw.BasicJoint;
import net.phys2d.raw.Body;
import net.phys2d.raw.ElasticJoint;
import net.phys2d.raw.FixedJoint;
import net.phys2d.raw.Joint;
import net.phys2d.raw.SpringJoint;
import net.phys2d.raw.StaticBody;
import net.phys2d.raw.shapes.PCircle;
import net.phys2d.raw.shapes.PhysShape;

public abstract class AbstractConstraintMaker 
extends AbstractPhysTool {
	
	protected Vector2f start;
	protected Vector2f end;
	protected boolean noScreen = false;
	protected boolean snapToBodies = false;
	protected boolean noCollide = false;
	protected boolean autoMake = false;
	
	protected CheckBox snapCheck;
	protected CheckBox ncCheck;
	protected CheckBox autoBodCheck;
	protected Slider breakLength;
	protected float flbreakLength = 0;
	protected KeyAssign breakKeyAssign;
	
	protected Joint joint = null;
	private int breakKey = -1;
	
	public AbstractConstraintMaker() {
		setGhostColor(GHOSTCOLOR_CONSTRAINT);
		setGhostOutlineSize(2);
		
		category = "Constraints";
		start = new Vector2f();
		end = new Vector2f();
		setDescription("Click and draw out a line between\n" +
				"two objects, or one object and the screen\n" +
				"to create a constraint.");
		
		snapCheck = new CheckBox("Snap To Center",false) {
			private static final long serialVersionUID = 1L;

			public void valueChanged(boolean b) {
				snapToBodies = b;
			}
		};
		addProperty(snapCheck);
		
		ncCheck = new CheckBox("NoCollide",false) {
			private static final long serialVersionUID = 1L;

			public void valueChanged(boolean b) {
				noCollide = b;
			}
		};
		addProperty(ncCheck);
		
		autoBodCheck = new CheckBox("AutoMake",false) {
			private static final long serialVersionUID = 1L;

			public void valueChanged(boolean b) {
				autoMake = b;
			}
		};
		addProperty(autoBodCheck);
		
		breakKeyAssign = new KeyAssign("Break") {
			public void valueChanged(int value, char ch) {
				breakKey = value;
			}
		};
		addProperty(breakKeyAssign);
		
		/*breakLength = new Slider("Break Length",0,0,200,20,true) {
			private static final long serialVersionUID = 1L;

			public void valueChanged(int value) {
				flbreakLength = value;
			}			
		};
		addProperty(breakLength);*/
	}
	
	public GeneralPath getGhostPath() {
		GeneralPath gp = new GeneralPath();
		gp.moveTo(start.x,start.y);
		gp.lineTo(end.x,end.y);
		gp.lineTo(end.x,end.y + 5);
		
		gp.moveTo(end.x,end.y);
		gp.lineTo(end.x,end.y - 5);
		
		gp.moveTo(end.x,end.y);
		gp.lineTo(end.x + 5,end.y);
		
		gp.moveTo(end.x,end.y);
		gp.lineTo(end.x - 5,end.y);
		return gp;
	}
	
	public void startTool(MouseEvent arg0) {
		super.startTool(arg0);
		start.x = arg0.getX();
		start.y = arg0.getY();
		end.x = arg0.getX();
		end.y = arg0.getY();
		inUse = true;
	}
	
	public void toolThink(MouseEvent arg0) {
		end.x = arg0.getX();
		end.y = arg0.getY();		
	}
	
	public void createJoint() {
		Body b1 = Util.selectBody(start.x,start.y,getWorld());
		Util.addToS_ExcludeList(b1);
		Util.clearHandle(getWorld());
		Body b2 = Util.selectBody(end.x,end.y,getWorld());
		Util.addToS_ExcludeList(b2);
		Util.clearHandle(getWorld());
		Util.clearS_ExcludeList();
		if(snapToBodies) {
			if(b1 != null) start = (Vector2f) b1.getPosition();
			if(b2 != null) end = (Vector2f) b2.getPosition();
		}
		if(noCollide) {
			if(b1 != null && b2 != null) {
				b1.addExcludedBody(b2);
				b2.addExcludedBody(b1);
			}
		}
		boolean createdAnchor1 = false;
		boolean createdAnchor2 = false;
		if(autoMake) {
			if(b2 == null) {
				b2 = createAnchorBody(end,false);
				createdAnchor2 = true;
			}
			if(b1 == null) {
				b1 = createAnchorBody(start,false);
				createdAnchor1 = true;
			}
			if(createdAnchor1 || createdAnchor2) {
				//b1.addExcludedBody(b2);
				//b2.addExcludedBody(b1);
			}
		}
		if(b1 != null) {
			if(!noScreen) {
				if(!(createdAnchor1 && createdAnchor2)) {
					if(b2 == null || b2 == b1) {
						b2 = createAnchorBody(end);
						b1.addExcludedBody(b2);
						b2.setShouldCollide(false);
						createdAnchor2 = true;
					}
				}
			}
			if(joint == null) {
				joint = createConstraint(b1, b2);
				joint.setMaxTension(flbreakLength);
				
				final Undo undo = new Undo(joint,getWorld(),"Constraint");
				if(createdAnchor2) undo.groupUndo(new Undo(b2,getWorld(),"Anchor"));
				if(createdAnchor1) undo.groupUndo(new Undo(b1,getWorld(),"Anchor"));
				Sandbox.getUndoList().add(undo);
				getWorld().add(joint);
				
				if(breakKey != -1) {
					final KeyTrigger trigger = new KeyTrigger(breakKey) {
						public void keyReleased() {
							undo.performUndo();
						}
					};
					Sandbox.addKeyTrigger(trigger);
					undo.groupUndo(new Undo(trigger,getWorld(),"Key Trigger") {
						public void performUndo() {
							Sandbox.removeKeyTrigger(trigger);
						}
					});
				}
				
				joint = null;
			}
		}
	}
	
	protected Joint createConstraint(Body b1, Body b2) {
		return null;
	}
	
	public void endTool(MouseEvent arg0) {
		createJoint();
		start = new Vector2f();
		end = new Vector2f();
		super.endTool(arg0);
	}

	protected Body createAnchorBody(Vector2f pos) {
		return createAnchorBody(pos, true);
	}
	
	protected Body createAnchorBody(Vector2f pos, boolean isStatic) {
		PhysShape shape = new PCircle(1.5f);
		Body b = null;
		if(isStatic) {
			b = new StaticBody(shape);
		} else {
			b = new Body(shape,5);
		}
		b.setPosition(pos.x,pos.y);
		getWorld().add(b);
		return b;
	}
}
